home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / bin / amarok_daapserver.rb < prev    next >
Text File  |  2008-08-13  |  15KB  |  419 lines

  1. #!/usr/bin/env ruby
  2.  
  3. #A DAAP Server
  4. # (c) 2006 Ian Monroe <ian@monroe.nu>
  5. # License: GNU General Public License V2
  6. $LOAD_PATH.push(ARGV[0])
  7. puts "here it is: #{ARGV[0]}"
  8. $LOAD_PATH.push(ARGV[1])
  9. puts "here it is: #{ARGV[1]}"
  10.  
  11. require "codes.rb"
  12. require 'mongrel'
  13. require "#{ARGV[2]}" #debug.rb
  14.  
  15. require 'uri'
  16. require 'pp'
  17. #require 'ruby-prof'
  18.  
  19. $app_name = "Daap"
  20. $debug_prefix = "Server"
  21.  
  22. class Element
  23.     attr_accessor :name
  24.  
  25.     public
  26.         def initialize(name, value = Array.new)
  27.             @name, @value = name, value
  28.         end
  29.  
  30.         def to_s( codes = nil )
  31.             if @value.nil? then
  32.                 log @name + ' is null'
  33.                 @name + Element.long_convert( 0 )
  34.             else
  35.                 content = valueToString( codes )
  36.                 @name + Element.long_convert(content.length) + content
  37.             end
  38.         end
  39.  
  40.         def collection?
  41.             @value.class == Array
  42.         end
  43.  
  44.         def <<( child )
  45.             @value << child
  46.         end
  47.  
  48.         def size
  49.             @value.size
  50.         end
  51.  
  52.         def Element.char_convert( v )
  53.             packing( v, 'c' )
  54.         end
  55.  
  56.         def Element.short_convert( v )
  57.             packing( v, 'n' )
  58.         end
  59.  
  60.         def Element.long_convert( v )
  61.             packing( v, 'N' )
  62.         end
  63.  
  64.         def Element.longlong_convert( v )
  65.             v = v.to_i  if( v.is_a?(String) )
  66.             a = Array.new
  67.             a[0] = v >> 32
  68.             b = Array.new
  69.             b[0] = v & 0xffffffff
  70.             a.pack('N') + b.pack('N')
  71.         end
  72.  
  73.     protected
  74.         def valueToString( codes )
  75.             case CODE_TYPE[@name]
  76.                 when :string then
  77.                     @value
  78.                 when :long then
  79.                     Element.long_convert( @value )
  80.                 when :container then
  81.                     values = String.new
  82.                     @value.each do |i|
  83.                         values += i.to_s( codes )
  84.                     end
  85.                     values
  86.                 when :char then
  87.                     Element.char_convert( @value )
  88.                 when :short then
  89.                     Element.short_convert( @value )
  90.                 when :longlong then
  91.                     Element.longlong_convert( @value )
  92.                 when :date then
  93.                     Element.long_convert( @value )
  94.                 when :version then
  95.                     Element.short_convert( @value )
  96.                 else
  97.                     log "type error! #{@value} #{CODE_TYPE[@name]} #{@name}"
  98.             end
  99.         end
  100.  
  101.         def Element.packing( v, packer )
  102.             v = v.to_i  if( v.is_a?(String) )
  103.             a = Array.new
  104.             a[0] = v
  105.             a.pack(packer)
  106.         end
  107.  
  108. end
  109.  
  110. class Mlit < Element
  111.     attr_accessor :songformat, :id
  112.     def to_s( codes )
  113.         values = String.new
  114.         @value.each { |i|
  115.             values += i.to_s( codes ) if codes.member?( i.name )
  116.         }
  117.         'mlit' + Element.long_convert(values.length) + values
  118.     end
  119. end
  120.  
  121. class DatabaseServlet < Mongrel::HttpHandler
  122.   include DebugMethods
  123.   public
  124.  
  125.       def initItems
  126.           @@sessionId = 42
  127.           artists = Hash.new
  128.           albums = Hash.new
  129.           genre = Hash.new
  130.           year = Hash.new
  131.           device_paths = Hash.new
  132.           indexes = [  { :dbresult=> query( 'select * from album' ),  :indexed => albums },
  133.           { :dbresult=> query( 'select * from artist' ), :indexed => artists },
  134.           { :dbresult=> query( 'select * from genre' )  , :indexed => genre },
  135.           { :dbresult=> query( 'select * from year' )  , :indexed => year },
  136.           { :dbresult=> query( 'select id, lastmountpoint from devices' ), :indexed => device_paths } ]
  137.           indexes.each { |h|
  138.               0.step( h[ :dbresult ].size, 2 ) { |i|
  139.                   h[ :indexed ][ h[ :dbresult ][i].to_i ] = h[ :dbresult ][ i.to_i+1 ]
  140.               }
  141.           }
  142.  
  143.           columns =     [ "album, ", "artist, ", "genre, ", "year, ", "track, ", "title, ", "length, ", "samplerate, ", "url, ", "deviceid" ]
  144.           puts "SQL QUERY: SELECT #{columns.to_s} FROM tags"
  145.           @column_keys = [ :songalbum, :songartist, :songgenre, :songyear, :songtracknumber, :itemname, :songtime, :songsamplerate, :url,  :deviceid ]
  146.           #TODO composer :songcomposer
  147.           @music = Array.new
  148.           @items = Element.new( 'mlcl' )
  149.           id = 0
  150.           columnIt = 0
  151.           track = Mlit.new( 'mlit' )
  152.           url = String.new
  153.           while ( line = $stdin.gets ) && ( line.chop! != '**** END SQL ****' )
  154.               puts "#{columnIt} - #{line}" if id < 10
  155.               case columnIt
  156.               when 0..3
  157.                   track << Element.new( METAS[ @column_keys[columnIt] ][ :code ], indexes[columnIt][ :indexed ][ line.to_i ] )
  158.               when (4 ..( @column_keys.size-3 ) )
  159.                   track << Element.new( METAS[ @column_keys[columnIt] ][ :code ], line )
  160.               when columns.size - 2
  161.                   url = line.reverse.chop.reverse
  162.               when columns.size - 1
  163.                   id += 1
  164.                   device_id = line.to_i
  165.                   if device_id == -1 then
  166.                       @music[id] = url
  167.                   else
  168.                       url[0] = ''
  169.                       @music[id] = "#{indexes.last[:indexed][ device_id ]}/#{url}"
  170.                   end
  171.                   track << Element.new( 'miid', id )
  172.                   track << Element.new( 'asfm', File::extname( url ).reverse.chop.reverse )
  173.                   @items << track
  174.                   columnIt = -1
  175.                   track = Mlit.new( 'mlit' )
  176.                   url = String.new
  177.               end
  178.               columnIt += 1
  179.           end
  180.           @column_keys.push( :itemid )
  181.           @column_keys.push( :songformat )
  182.       end
  183.       debugMethod(:new)
  184.  
  185.       def process( request, response )
  186.           if @items.nil? then
  187.               initItems()
  188.           end
  189.           uri = URI::parse( request.params["REQUEST_URI"] )
  190.           command = File.basename( uri.path )
  191.           output = String.new
  192.           case command
  193.               #{"mupd"=>{"mstt"=>[200], "musr"=>[2]}}
  194.               when "login" then
  195.                   root =  Element.new( 'mlog' )
  196.                   root << Element.new( 'mlid', @@sessionId )
  197.                   root << Element.new( 'mstt',  200 ) #200, as in the HTTP OK code
  198.                   write_resp( response, root.to_s )
  199.                   @@sessionId += 1
  200.               #{"mupd"=>{"mstt"=>[200], "musr"=>[2]}}
  201.               when "update" then
  202.                   root = Element.new( 'mupd' )
  203.                   root << Element.new( 'mstt', 200 )
  204.                   root << Element.new( 'musr', 2 )
  205.                   write_resp( response, root.to_s )
  206.               when 'server-info'
  207. # SimpleDaapClient output from Banshee server
  208. #               {"msrv"=>
  209. #                 {"mpro"=>[131074],
  210. #                 "msbr"=>[nil],
  211. #                 "mslr"=>[nil],
  212. #                 "msup"=>[nil],
  213. #                 "msex"=>[nil],
  214. #                 "msqy"=>[nil],
  215. #                 "msau"=>[nil],
  216. #                 "apro"=>[196610],
  217. #                 "minm"=>["Banshee Music Share"],
  218. #                 "msdc"=>[1],
  219. #                 "mstt"=>[200],
  220. #                 "msal"=>[nil],
  221. #                 "msrs"=>[nil],
  222. #                 "mstm"=>[1800],
  223. #                 "mspi"=>[nil],
  224. #                 "msix"=>[nil]}}
  225.                   msrv = Element.new( 'msrv' )
  226.                     msrv << Element.new( 'mpro', 0x20002 )
  227.                     msrv << Element.new( 'msbr', 1 )
  228.                     msrv << Element.new( 'mslr', 1 )
  229.                     msrv << Element.new( 'msup', 1 )
  230.                     msrv << Element.new( 'msex', 1 )
  231.                     msrv << Element.new( 'msqy', 1 )
  232.                     msrv << Element.new( 'msau', 0 )
  233.                     msrv << Element.new( 'apro', 0x30002 )
  234.                     msrv << Element.new( 'minm', "Amarok Music Share" )
  235.                     msrv << Element.new( 'msdc', 1 )
  236.                     msrv << Element.new( 'mstt', 200 )
  237.                     msrv << Element.new( 'msal', 1 )
  238.                     msrv << Element.new( 'msrs', 1 )
  239.                     msrv << Element.new( 'mstm', 1800 )
  240.                     msrv << Element.new( 'mspi', 1 )
  241.                     msrv << Element.new( 'msix', 1 )
  242.                   write_resp( response, msrv.to_s )
  243.               when 'content-codes' then
  244.                   write_resp( response, CONTENT_CODES ) #LAAAAAZY
  245.               when 'containers' then
  246.               # {"aply"=>
  247.               #   {"muty"=>[nil],
  248.               #    "mstt"=>[200],
  249.               #    "mrco"=>[1],
  250.               #    "mtco"=>[1],
  251.               #    "mlcl"=>
  252.               #     [{"mlit"=>
  253.               #        [{"abpl"=>[nil],
  254.               #          "miid"=>[1],
  255.               #          "mper"=>[0],
  256.               #          "minm"=>["Banshee Music Share"],
  257.               #          "mimc"=>[2463]}]}]}}
  258.                 aply = Element.new( 'aply' )
  259.                     aply << Element.new( 'muty', nil )
  260.                     aply << Element.new( 'mstt', 200 )
  261.                     aply << Element.new( 'mrco', 1 )
  262.                     mlcl =  Element.new( 'mlcl')
  263.                     aply << mlcl
  264.                         mlit = Element.new( 'mlit' )
  265.                         mlcl << mlit
  266.                             mlit << Element.new( 'abpl', nil )
  267.                             mlit << Element.new( 'miid', 1 )
  268.                             mlit << Element.new( 'mper', 0 )
  269.                             mlit << Element.new( 'minm', "Amarok Music Share" )
  270.                             mlit << Element.new( 'mimc', @items.size )
  271.                 write_resp( response, aply.to_s )
  272.               when "databases" then
  273.               # {"avdb"=>
  274.               #   {"muty"=>[nil],
  275.               #    "mstt"=>[200],
  276.               #    "mrco"=>[1],
  277.               #    "mtco"=>[1],
  278.               #    "mlcl"=>
  279.               #     [{"mlit"=>
  280.               #        [{"miid"=>[1],
  281.               #          "mper"=>[0],
  282.               #          "minm"=>["Banshee Music Share"],
  283.               #          "mctc"=>[1],
  284.               #          "mimc"=>[1360]}]}]}}
  285.                   avdb = Element.new( 'avdb' )
  286.                   avdb << Element.new( 'muty', 0 )
  287.                   avdb << Element.new( 'mstt', 200 )
  288.                   avdb << Element.new( 'mrco', 1 )
  289.                   avdb << Element.new( 'mtco', 1 )
  290.                   mlcl = Element.new( 'mlcl' )
  291.                   avdb << mlcl
  292.                       mlit = Element.new( 'mlit' )
  293.                       mlcl << mlit
  294.                       mlit << Element.new( 'miid', 1 )
  295.                       mlit << Element.new( 'mper', 0 )
  296.                       mlit << Element.new( 'minm', ENV['USER'] + " Amarok" )
  297.                       mlit << Element.new( 'mctc', 1 )
  298.                       mlit << Element.new( 'mimc', @items.size )
  299.                   write_resp( response, avdb.to_s )
  300.               when "items" then
  301.               # {"adbs"=>
  302.               #     {"muty"=>[nil],
  303.               #     "mstt"=>[200],
  304.               #     "mrco"=>[1360],
  305.               #     "mtco"=>[1360],
  306.               #     "mlcl"=>
  307.               #         [{"mlit"=>
  308.               #             {"asal"=>["Be Human: Ghost in the Shell"],
  309.               #             "miid"=>[581],
  310.               #             "astm"=>[86000],
  311.               #             "minm"=>["FAX me"],
  312.               #             "astn"=>[nil],
  313.               #             "asar"=>["Yoko Kanno"],
  314.               #             "ascm"=>[""]},
  315.               #             ...
  316.                   requested = uri.query.nil? ? Array.new : Mongrel::HttpRequest.query_parse( uri.query )['meta'].split(',')
  317.                   puts "#{request.params.inspect} #{requested.inspect} #{uri.to_s} #{request.params["REQUEST_URI"]}"
  318.                   toDisplay =  Array.new
  319.                   requested.each { |str|
  320.                       str[0,5] = ''
  321.                       index = str.to_sym
  322.                       if @column_keys.include?( index ) then
  323.                           if( METAS[ index ] )
  324.                               toDisplay.push( METAS[ index ][:code] )
  325.                           else
  326.                               log "not being displayed #{index.to_s}"
  327.                           end
  328.                       end
  329.                   }
  330.                   adbs = Element.new( 'adbs' )
  331.                   adbs << Element.new( 'muty', nil )
  332.                   adbs << Element.new( 'mstt', 200 )
  333.                   adbs << Element.new( 'mrco', @items.size )
  334.                   adbs << Element.new( 'mtco', @items.size )
  335.                   adbs << @items
  336.                   write_resp( response, adbs.to_s( toDisplay ) )
  337.               else if command =~ /([\d]*)\.(.*)$/ #1232.mp3
  338.                       log "sending #{@music[ $1.to_i ]}"
  339.                       file = @music[ $1.to_i ]
  340.                       response.start(200) do |head,out|
  341.                           response.send_status(File.size( file ))
  342.                           response.header['Content-Type'] = "application/#{$2}"
  343.                           response.send_header
  344.                           response.send_file(file)
  345.                       end
  346.                       response.send_header
  347.                       response.send_file( file )
  348.                   else
  349.                       response.start( 404 ) do | head, out |
  350.                         out << "Command not implemented."
  351.                       end
  352.                       puts "#{command} not implemented"
  353.                   end
  354.           end
  355.       end
  356.       debugMethod(:do_GET)
  357.  
  358.   private
  359.       def query( sql )
  360.           out = Array.new
  361.           puts "SQL QUERY: #{sql}"
  362.           while ( line = $stdin.gets) && (line.chop! != '**** END SQL ****' )
  363.               out.push( line )
  364.           end
  365.           out
  366.       end
  367.       debugMethod(:query)
  368.  
  369.       def write_resp( response, value )
  370.           response.start do | head, out |
  371.               head['DAAP-Server'] = 'amarok-kaylee'
  372.               head['Content-Type'] = 'application/x-dmap-tagged'
  373.               out << value
  374.           end
  375.       end
  376. end
  377.  
  378. def log( string )
  379.   f = open('/tmp/test.ruby', File::WRONLY | File::APPEND | File::CREAT )
  380.   f.puts( string )
  381.   f.close
  382. end
  383.  
  384. class Controller
  385.  
  386.     def initialize
  387.         port = 3689
  388.         no_server = true
  389.         while no_server
  390.             begin
  391.                 server = Mongrel::HttpServer.new('0.0.0.0', port)
  392.                 no_server = false
  393.             rescue Errno::EADDRINUSE
  394.                 if port == 3700 then
  395.                     fatal( "No ports between 3688 and 3700 are open." )
  396.                 end
  397.                 port += 1
  398.             end
  399.         end
  400.         ds = DatabaseServlet.new
  401.         server.register('/', ds )
  402.         server.register('daap', ds )
  403.         puts "SERVER STARTING: #{port}"
  404.         server.run.join
  405.     end
  406.  
  407. end
  408.  
  409. $stdout.sync = true
  410. $stderr.sync = true
  411.  
  412. #RubyProf.start
  413.     Controller.new
  414.  
  415. #result = RubyProf.stop
  416. #printer = RubyProf::GraphHtmlPrinter.new(result)
  417. #f = open('/tmp/test.html', File::WRONLY | File::CREAT )
  418. #printer.print( f, 3 )
  419.